--------------------------
-- FUNCTION (makeBillboard)
-- 
-- This function realizes the billboard effect: an object looks at another object, but only rotates
-- about its World z-axis. This is accomplished by assigning a script controller to the object's rotation.
-- Through the use of a little trigonometry, the rotation is set.
-- The billboard’s Local z-axis defines the front of the object. This convention is chosen to match the
-- orientation of objects created in the front view.
--
-- This function is based coded posted to the MAXScript WebBoard by Larry Minton.

fn makeBillboard
	obj		-- The billboard object.
	targ	-- The lookat object.
=
(
setWaitCursor()
-- Check to ensure the objects are defined and not deleted.
if obj!=undefined AND (NOT isDeleted obj) AND targ!=undefined AND (NOT isDeleted targ) then
(
-- Build string to be used in script controller on obj's rotation.
-- Notes:
--  - Script is in one big 'try/catch'. So, if any error occurs, the catch clause is executed,
--    and rotation is effectively set to [0,0,0].
--  - 'dependsOn' signals the controller to update when another controller changes.
--    In this case, the rotation changes when the position of the lookat object changes.
--    So, on a given frame, when the user moves the lookat object, the billboard rotates.
--  - An object name is put in single quotes (e.g. $'An Object Name') in case spaces or other
--    "problem" characters are contained in the name. Without the single quotes, an error will occur.
--  - The script controller returns a quaternion rotation value: see the lines containing
--    'eulerAngles' (pronounced "oiler angles").
--  - Basic trigonometry is used to calculate the rotation angle required to achieve the billboard effect.

local scriptStr=
"try(
dependsOn $'"+targ.name+"'.pos.controller

local d, dz, z_rot, len, ctrl

-- Get difference vector of obj and targ.
d=
try ($'"+obj.name+"'.pos-$'"+targ.name+"'.pos)
catch [0,0,0]

dz=d.z -- Store z value.
d.z=0 -- Set z to zero.
len=length d -- Vector length.

-- Calculate the z rotation (in degrees).
-- Note: len==0 => obj and targ are at same position!
z_rot=
if len!=0
	then -90+acos(d.x/len)*(if d.y!=0 then d.y/(abs d.y) else 1)
	else -90
(eulerAngles 90 0 z_rot) as quat
)catch ((eulerAngles 90 0 0) as quat)"

ctrl=obj.rotation.controller=rotation_script() -- Assign rotation script controller.
setTimeRange ctrl (interval -1000 10000) -- Set script controller's range bar to wide range, in case user expands range later.
ctrl.script=scriptStr -- Put script string into script controller.
)--end main 'if/then'.

setArrowCursor()
)
--------------------------